<?php
/**
 * 框架 - 简单的[ActiveRecord]模式
 *
 * @version $Id: ActiveRecord.php 157 2013-12-24 02:43:50Z linsir123 $
 * @package ActiveRecord
 */
class ActiveRecord
{
	/**
	 * 数据表连接关系
	 */
	const LEFT_JOIN = 'LEFT JOIN';

	/**
	 * 数据表连接关系
	 */
	const RIGHT_JOIN = 'RIGHT JOIN';

	/**
	 * 数据表连接关系
	 */
	const INNER_JOIN = 'INNER JOIN';

	/**
	 * 数据库表名,若没配置则可使用{@setTable}方法进行配置
	 * // 没有配置些属性,在使用过程序系统会抛出异常
	 *
	 * @var string
	 */
	public $table;

	/**
	 * 连接数据源序号
	 * // 当有多个数据源时,需要配置
	 *
	 * @var int
	 */
	public $mysqlIndex = 0;

	/**
	 * 数据查询对象
	 *
	 * @var ARSelect
	 */
	public $_oSelect;

	/**
	 * 数据操作对象
	 *
	 * @var ARCommand
	 */
	public $_oCommand;

	/**
	 * 数据操作或查询使用的条件对象
	 *
	 * @var ARCondition
	 */
	public $_oCondition;

	/**
	 * 最近一次SQL
	 *
	 * @var string
	 */
	private $_lastSql;

	/**
	 * 构造函数
	 */
	public function __construct()
	{
		///
		$this->_oCondition = new ARCondition($this->table);

		///
		$this->_oSelect = new ARSelect($this->table, $this->_oCondition);

		///
		$this->_oCommand = new ARCommand($this->table, $this->_oCondition);
	}

	/**
	 * 设置数据表名
	 * // 分表时需要重新设置
	 *
	 * @param string $table 数据表
	 */
	public function setTable($table)
	{
		///
		$this->table = $table;

		///
		$this->_oSelect->setTable($table);
		$this->_oCommand->setTable($table);
		$this->_oCondition->setTable($table);
	}

	/**
	 * 设置数据源序号
	 * // 分库时需要重新设置
	 *
	 * @param int $index 连接数据源序号
	 */
	public function setMysqlIndex($index = 0)
	{
		///
		$this->mysqlIndex = $index;
	}

	/**
	 * 插入数据
	 *
	 * @param array $data 待插入的数据
	 *
	 * @return int 影响行数
	 */
	public function insert($data)
	{
		///
		$sql = $this->_oCommand->insert($data);

		///
		return $this->query($sql);
	}

	/**
	 * 插入数据
	 * // 若主键存在则执行“ON DUPLICATE KEY UPDATE”
	 *
	 * @param array $data 待插入的数据
	 * @param array $duplicateData 需要执行更新的数据
	 *
	 * @return int 影响行数
	 */
	public function insertDuplicate($data, $duplicateData)
	{
		///
		$sql = $this->_oCommand->insertDuplicate($data, $duplicateData);

		///
		return $this->query($sql);
	}

	/**
	 * 删除数据
	 * // 需要先调用Where再调用此操作进行删除
	 *
	 * @return int 影响行数
	 */
	public function delete()
	{
		///
		$sql = $this->_oCommand->delete();

		///
		return $this->query($sql);
	}

	/**
	 * 更新数据
	 * // 需要先调用Where再调用此操作进行更新
	 *
	 * @param array $data 待更新的数据
	 *
	 * @return int 影响行数
	 */
	public function update($data)
	{
		///
		$sql = $this->_oCommand->update($data);

		///
		return $this->query($sql);
	}

	/**
	 * [Select]设置重复
	 *
	 * @return ActiveRecord
	 */
	public function distinct()
	{
		$this->_oSelect->set('DISTINCT', true);

		///
		return $this;
	}

	/**
	 * [Select]设置操作条件
	 *
	 * @return ActiveRecord
	 */
	public function where()
	{
		$args = func_get_args();
		$this->_oCondition->add($args, true);

		///
		return $this;
	}

	/**
	 * [Select]设置操作条件
	 *
	 * @return ActiveRecord
	 */
	public function orWhere()
	{
		$args = func_get_args();
		$this->_oCondition->add($args, false);

		///
		return $this;
	}

	/**
	 * [Select]设置聚合
	 *
	 * @param string $field 聚合的字段
	 * @param bool|int $preTable 构造SQL时，是否添加表前缀(聚合查询时不需要)
	 *
	 * @return ActiveRecord
	 */
	public function group($field, $preTable = true)
	{
		$this->_oSelect->set('GROUP', array($field, $preTable));

		///
		return $this;
	}

	/**
	 * [Select]设置查询区间
	 *
	 * @param int $start 开始
	 * @param int $offset 区间
	 *
	 * @return ActiveRecord
	 */
	public function limit($start, $offset)
	{
		$this->_oSelect->set('LIMIT', array($start, $offset));

		///
		return $this;
	}

	/**
	 * [Select]设置查询字段
	 *
	 * @param string $field 查询的字段
	 *
	 * @return ActiveRecord
	 */
	public function field($field)
	{
		$this->_oSelect->push('FIELD', $field);

		///
		return $this;
	}

	/**
	 * [Select]设置查询排序
	 *
	 * @param string $field 排序的字段
	 * @param int $index 序号(用于定义排序顺序)
	 * @param bool|int $preTable 构造SQL时，是否添加表前缀(聚合查询时不需要)
	 *
	 * @return ActiveRecord
	 */
	public function order($field, $index = 0, $preTable = true)
	{
		$this->_oSelect->push('ORDER', array($field, $index, $preTable));

		///
		return $this;
	}

	/**
	 * 左连接
	 *
	 * @param ActiveRecord $ar 被连接的模型
	 * @param string $key 主键名
	 * @param string $fKey 外键名
	 *
	 * @return ActiveRecord
	 */
	public function joinLeft($ar, $key, $fKey)
	{
		$this->_join(self::LEFT_JOIN, $ar, $key, $fKey);

		///
		return $this;
	}

	/**
	 * 右连接
	 *
	 * @param ActiveRecord $ar 被连接的模型
	 * @param string $key 主键名
	 * @param string $fKey 外键名
	 *
	 * @return ActiveRecord
	 */
	public function joinRight($ar, $key, $fKey)
	{
		$this->_join(self::RIGHT_JOIN, $ar, $key, $fKey);

		///
		return $this;
	}

	/**
	 * 内连接
	 *
	 * @param ActiveRecord $ar 被连接的模型
	 * @param string $key 主键名
	 * @param string $fKey 外键名
	 *
	 * @return ActiveRecord
	 */
	public function joinInner($ar, $key, $fKey)
	{
		$this->_join(self::INNER_JOIN, $ar, $key, $fKey);

		///
		return $this;
	}

	/**
	 * 子查询
	 *
	 * @param ActiveRecord $ar 被连接的模型
	 *
	 * @return ActiveRecord
	 */
	public function fromSub($ar)
	{
		$this->_oSelect->fromSub($ar->_oSelect);

		///
		return $this;
	}

	/**
	 * 取得单行列记录值
	 *
	 * @param string $sql 查询SQL
	 *
	 * @return mixed
	 */
	public function getVar($sql = '')
	{
		$sql = $this->_toSql($sql);

		///
		return $this->_getMysql()->getVar($sql);
	}

	/**
	 * 取得某列记录
	 *
	 * @param string $sql 查询SQL
	 *
	 * @return mixed
	 */
	public function getCol($sql = '')
	{
		$sql = $this->_toSql($sql);

		///
		return $this->_getMysql()->getCol($sql);
	}

	/**
	 * 取得多行记录
	 *
	 * @param string $sql 查询SQL
	 *
	 * @return mixed
	 */
	public function getResults($sql = '')
	{
		$sql = $this->_toSql($sql);

		///
		return $this->_getMysql()->getResults($sql);
	}

	/**
	 * 取得单行记录
	 *
	 * @param string $sql 查询SQL
	 *
	 * @return mixed
	 */
	public function getRow($sql = '')
	{
		if (empty($sql)) {
			if (! $this->_oSelect->hadSet('LIMIT'))
				$this->limit(0, 1);

			$sql = $this->_toSql();
		}

		///
		return $this->_getMysql()->getRow($sql);
	}

	/**
	 * 返回最近一次的SQL
	 *
	 * @return string
	 */
	public function getLastSql()
	{
		return $this->_lastSql;
	}

	/**
	 * 返回某个SQL表达式对象
	 *
	 * @param string $string
	 *
	 * @return FieldExpression
	 */
	public function getExpression($string)
	{
		return new FieldExpression($string);
	}

	/**
	 * 执行数据操作SQL
	 *
	 * @param string $sql 待执行的SQL
	 *
	 * @throws Exception
	 *
	 * @return mixed
	 */
	function query($sql)
	{
		///
		if (empty($this->table))
			throw new Exception("[ActiveRecord] 模型数据表名未设置");

		///
		$this->_lastSql = $sql;

		///
		return $this->_getMysql()->query($sql);
	}

	/**
	 * 返回查询SQL
	 *
	 * @param string $sql
	 *
	 * @throws Exception
	 *
	 * @return string
	 */
	protected function _toSql($sql = '')
	{
		///
		if (empty($this->table))
			throw new Exception("[ActiveRecord] 模型数据表名未设置");

		///
		$this->_lastSql = empty($sql) ? $this->_oSelect->__toString() : $sql;

		///
		return $this->_lastSql;
	}

	/**
	 * 取得Mysql连接对象
	 * // 子类可以对方法进行重写
	 *
	 * @return LpMysql
	 */
	protected function _getMysql()
	{
		///
		return App::getMysql($this->mysqlIndex);
	}

	/**
	 * 连接
	 *
	 * @param string $type 类型
	 * @param ActiveRecord $ar 被连接的模型
	 * @param string $key 主键名
	 * @param string $fKey 外键名
	 */
	private function _join($type, $ar, $key, $fKey)
	{
		///
		$this->_oSelect->join($ar->_oSelect, $type, $key, $fKey);
	}
}
